package com.share.device.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.share.common.core.context.SecurityContextHolder;
import com.share.common.core.domain.R;
import com.share.common.core.exception.ServiceException;
import com.share.common.core.utils.StringUtils;
import com.share.common.core.utils.bean.BeanUtils;
import com.share.common.rabbit.service.RabbitService;
import com.share.device.domain.*;
import com.share.device.emqx.EmqxClientWrapper;
import com.share.device.emqx.constant.EmqxConstants;
import com.share.device.mapper.CabinetMapper;
import com.share.device.mapper.CabinetSlotMapper;
import com.share.device.mapper.PowerBankMapper;
import com.share.device.mapper.StationMapper;
import com.share.device.service.IDeviceService;
import com.share.device.service.IMapService;
import com.share.order.api.RemoteOrderInfoService;
import com.share.order.domain.OrderInfo;
import com.share.rule.api.RemoteFeeRuleService;
import com.share.rule.domain.FeeRule;
import com.share.user.api.RemoteUserInfoService;
import com.share.user.api.domain.UserInfo;
import lombok.RequiredArgsConstructor;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ZhouShuang
 * @version 17
 * @Title
 * @date 2025/10/13 下午11:19
 */
@Service
@RequiredArgsConstructor
public class DeviceServiceImpl implements IDeviceService {

    private final MongoTemplate mongoTemplate;
    private final StationMapper stationMapper;
    private final CabinetMapper cabinetMapper;
    private final IMapService mapService;
    private final RemoteFeeRuleService remoteFeeRuleService;
    private final RemoteUserInfoService remoteUserInfoService;
    private final RemoteOrderInfoService remoteOrderInfoService;
    private final EmqxClientWrapper emqxClientWrapper;
    private final CabinetSlotMapper cabinetSlotMapper;
    private final PowerBankMapper powerBankMapper;
    private final RabbitService rabbitService;
    private final CabinetSlotService cabinetSlotService;

    /**
     * 获取附近站点的信息列表
     *
     * @param latitude
     * @param longitude
     * @return
     */
    @Override
    public List<StationVo> nearByStationList(String latitude, String longitude, Integer radius) {
        //确定中心经纬度
        GeoJsonPoint geoJsonPoint = new GeoJsonPoint(Double.parseDouble(longitude), Double.parseDouble(latitude));
        //以中心经纬度，为圆心，半径为5公里的圆
        Distance distance = new Distance(radius, Metrics.KILOMETERS);
        Circle circle = new Circle(geoJsonPoint, distance);

        //查询附近半径5公里内的站点，排除自己
        Query query = Query.query(Criteria.where("location").withinSphere(circle));
        List<StationLocation> stationLocationList = mongoTemplate.find(query, StationLocation.class);
        if (CollectionUtils.isEmpty(stationLocationList)) return null;

        //获取站点的详细信息
        List<Long> stationIds = stationLocationList.stream()
                .map(StationLocation::getStationId)
                .toList();

        //组装返回数据
        List<Station> stationList = stationMapper.selectList(new LambdaQueryWrapper<Station>()
                .in(Station::getId, stationIds).isNotNull(Station::getCabinetId)
                .eq(Station::getStatus, "1"));
        if (CollectionUtils.isEmpty(stationList)) return null;
        //获取站点列表对应的柜机id列表
        List<Long> cabinetIds = stationList.stream()
                .map(Station::getCabinetId)
                .toList();
        //构建map，key为柜机id，value为柜机信息
        Map<Long, Cabinet> cabinetMap = cabinetMapper.selectList(new LambdaQueryWrapper<Cabinet>().in(Cabinet::getId, cabinetIds))
                .stream()
                .filter(cabinet -> !ObjectUtils.isEmpty(cabinet))
                .collect(Collectors.toMap(Cabinet::getId, Cabinet -> Cabinet));
        if(cabinetMap.isEmpty()){
            return null;
        }

        //获取站点列表中对应的规则id列表
        List<Long> feeRuleIds = stationList.stream()
                .map(Station::getFeeRuleId)
                .toList();

        //构建map，key为规则id，value为规则信息
        Map<Long, String> feeRuleMap = remoteFeeRuleService.getFeeRuleList(feeRuleIds)
                .getData()
                .stream()
                .collect(Collectors.toMap(FeeRule::getId, FeeRule::getName));
        //准备返回数据
        List<StationVo> stationVoList = new ArrayList<>();
        //循环站点列表，设置可借用充电宝和可归还充电宝
        stationList.forEach(station -> {
            StationVo stationVo = new StationVo();
            BeanUtils.copyProperties(station, stationVo); //拷贝属性

            //计算距离
            Double calculateDistance = mapService.calculateDistance(longitude, latitude,
                    station.getLongitude().toString(), station.getLatitude().toString());
            stationVo.setDistance(calculateDistance);

            //可借用充电宝大于0，则可以借用
            stationVo.setIsUsable(cabinetMap.get(station.getCabinetId()).getAvailableNum() > 0 ? "1" : "0");
            //空闲插槽数量大于0，可归还
            stationVo.setIsReturn(cabinetMap.get(station.getCabinetId()).getFreeSlots() > 0 ? "1" : "0");
            //获取站点规则数据
            stationVo.setFeeRule(feeRuleMap.get(station.getFeeRuleId()));

            stationVoList.add(stationVo);
        });
        return stationVoList;
    }

    /**
     * 根据id获取门店详情
     *
     * @param id
     * @param latitude
     * @param longitude
     * @return
     */
    @Override
    public StationVo getStation(Long id, String latitude, String longitude) {
        Station station = stationMapper.selectById(id);
        StationVo stationVo = new StationVo();
        BeanUtils.copyProperties(station, stationVo);
        String description = remoteFeeRuleService
                .getFeeRule(station.getFeeRuleId())
                .getData()
                .getDescription();
        stationVo.setFeeRule(description);
        //计算距离
        Double calculateDistance = mapService.calculateDistance(longitude, latitude,
                station.getLongitude().toString(), station.getLatitude().toString());
        stationVo.setDistance(calculateDistance);
        Cabinet cabinet = cabinetMapper.selectById(station.getCabinetId());
        //可借用充电宝大于0，则可以借用
        stationVo.setIsUsable(cabinet.getAvailableNum() > 0 ? "1" : "0");
        //空闲插槽数量大于0，可归还
        stationVo.setIsReturn(cabinet.getFreeSlots() > 0 ? "1" : "0");

        return stationVo;
    }

    /**
     * 扫码充电
     *
     * @param cabinetNo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ScanChargeVo scanCharge(String cabinetNo) {
        //要返回的扫码充电对象
        ScanChargeVo scanChargeVo = new ScanChargeVo();

        //首先判断，用户是否免押金
        R<UserInfo> userInfoResult = remoteUserInfoService.getUserInfo(SecurityContextHolder.getUserId());
        System.out.println(userInfoResult.getMsg());
        if (R.FAIL == userInfoResult.getCode()) {
            throw new ServiceException(userInfoResult.getMsg());
        }
        UserInfo userInfo = userInfoResult.getData();
        if (ObjectUtils.isEmpty(userInfo)) {
            throw new ServiceException("获取用户失败");
        }

        //判断用户状态
        if ("2".equals(userInfo.getStatus())) {
            throw new ServiceException("用户已被禁用");
        }

        if ("0".equals(userInfo.getDepositStatus())) {
            throw new ServiceException("未申请免押金使用");
        }

        //判断用户是否有正在使用充电服务或者有未支付的订单
        R<OrderInfo> orderInfoResult = remoteOrderInfoService.getNoFinishOrder(SecurityContextHolder.getUserId());
        if (R.FAIL == orderInfoResult.getCode()) {
            throw new ServiceException(orderInfoResult.getMsg());
        }

        OrderInfo orderInfo = orderInfoResult.getData();
        if (!ObjectUtils.isEmpty(orderInfo)) {
            if ("0".equals(orderInfo.getStatus())) {
                scanChargeVo.setStatus("2");
                scanChargeVo.setMessage("有未归还的充电宝，请归还后使用");
                return scanChargeVo;
            }
            if ("1".equals(orderInfo.getStatus())) {
                scanChargeVo.setStatus("3");
                scanChargeVo.setMessage("有未支付订单，去支付");
                return scanChargeVo;
            }
        }

        //获取可用的充电宝信息
        AvailablePowerBankVo availablePowerBankVo = this.checkAvailablePowerBank(cabinetNo);
        if (ObjectUtils.isEmpty(availablePowerBankVo)) {
            throw new ServiceException("没有可用的充电宝");
        }

        if (!StringUtils.isEmpty(availablePowerBankVo.getErrMessage())) {
            throw new ServiceException(availablePowerBankVo.getErrMessage());
        }

        //生成指令，弹出充电宝
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("uId", SecurityContextHolder.getUserId());
        jsonObject.put("mNo", "mm" + RandomUtil.randomString(8));
        jsonObject.put("cNo", cabinetNo);
        jsonObject.put("pNo", availablePowerBankVo.getPowerBankNo());
        jsonObject.put("sNo", availablePowerBankVo.getSlotNo());
        String topic = String.format(EmqxConstants.TOPIC_SCAN_SUBMIT, cabinetNo);
        emqxClientWrapper.publish(topic, jsonObject.toJSONString());

        scanChargeVo.setStatus("1");
        scanChargeVo.setMessage("扫码成功");
        return scanChargeVo;
    }

    @Override
    public void unlockSlot(CabinetSlot cs) {
        CabinetSlot cabinetSlot = cabinetSlotService.getById(cs.getId());
        if ("2".equals(cabinetSlot.getStatus())) {
            //状态（1：占用 0：空闲 2：锁定）
            cabinetSlot.setStatus("1");
            cabinetSlot.setUpdateTime(new Date());
            cabinetSlotService.updateById(cabinetSlot);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public AvailablePowerBankVo checkAvailablePowerBank(String cabinetNo) {
        AvailablePowerBankVo availablePowerBankVo = new AvailablePowerBankVo();
        //获取柜机信息
        Cabinet cabinet = cabinetMapper.selectOne(new LambdaQueryWrapper<Cabinet>().eq(Cabinet::getCabinetNo, cabinetNo));
        //判断柜机是否在使用中
        if (ObjectUtils.isEmpty(cabinet)) {
            availablePowerBankVo.setErrMessage("柜机不存在");
            return availablePowerBankVo;
        }

        if (!cabinet.getStatus().equals("1")) {
            availablePowerBankVo.setErrMessage("该柜机故障或者未投入");
            return availablePowerBankVo;
        }

        if (cabinet.getAvailableNum() <= 0) {
            availablePowerBankVo.setErrMessage("该柜机没有可用充电宝");
            return availablePowerBankVo;
        }

        //获取柜机的可用插槽列表
        List<CabinetSlot> cabinetSlotList = cabinetSlotMapper.selectList(new LambdaQueryWrapper<CabinetSlot>()
                .eq(CabinetSlot::getCabinetId, cabinet.getId())
                .eq(CabinetSlot::getStatus, "1")); // 状态（1：占用 0：空闲 2：锁定）

        //获取插槽对应的可用充电宝信息
        List<Long> powerBankIds = cabinetSlotList.stream()
                .map(CabinetSlot::getPowerBankId)
                .filter(Objects::nonNull).toList();
        List<PowerBank> powerBankList = powerBankMapper.selectList(new LambdaQueryWrapper<PowerBank>()
                .in(PowerBank::getId, powerBankIds)
                .eq(PowerBank::getStatus, "1"));
        //判断充电宝列表是否为空或者长度为0
        if (CollectionUtils.isEmpty(powerBankList)) {
            availablePowerBankVo.setErrMessage("该柜机没有可用充电宝");
            return availablePowerBankVo;
        }
        //筛选出电量最多的充电宝
        if (powerBankList.size() == 1) {
            availablePowerBankVo.setPowerBankNo(powerBankList.get(0).getPowerBankNo());
        }
        PowerBank powerBank = powerBankList.stream().max(Comparator.comparing(PowerBank::getElectricity)).get();
        //获取电量最多充电宝的插槽信息
        CabinetSlot cabinetSlot = cabinetSlotMapper.selectOne(new LambdaQueryWrapper<CabinetSlot>().eq(CabinetSlot::getPowerBankId, powerBank.getId()));
        //更新插槽状态和充电宝状态
        cabinetSlot.setStatus("2");
        cabinetSlot.setUpdateTime(new Date());

        availablePowerBankVo.setSlotNo(cabinetSlot.getSlotNo());
        availablePowerBankVo.setPowerBankNo(powerBank.getPowerBankNo());

        // 扫码后未弹出充电宝等情况，延迟解锁
//        rabbitService.sendDealyMessage(MqConst.EXCHANGE_DEVICE,
//                MqConst.ROUTING_UNLOCK_SLOT,
//                JSONObject.toJSONString(cabinetSlot),
//                MqConst.CANCEL_UNLOCK_SLOT_DELAY_TIME);

        return availablePowerBankVo;
    }
}
